﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Threading;
using Timers = System.Timers;

namespace OTDR_OPT
{
    //连接异常断开时的事件数据
    public class ConnectBreakEvent : EventArgs
    {

    }


    //OTDR类，包括OTDR的光功的设置等信息
    public class Otdr : IOpticalPowerMeter
    {
        #region 全局变量
        private SerialPort serialPort = null;
        private AutoResetEvent changeWaveEvent = new AutoResetEvent(false);  //切换波长的信号量
        private AutoResetEvent readPowerEvent = new AutoResetEvent(false);  //读取光功的信号量
        private AutoResetEvent setModeEvent = new AutoResetEvent(false); //进入设置模式型号量
        private AutoResetEvent setLinearEvent = new AutoResetEvent(false);  //设置线性点信号量
        private AutoResetEvent setValueEvent = new AutoResetEvent(false);  //设置-10点的信号量
        private AutoResetEvent saveValueEvent = new AutoResetEvent(false); //写入EEPROM信号量
        private List<byte> receiveByteList = new List<byte>(); //收到的字节清单
        public EventHandler<ConnectBreakEvent> ConnectBreakHandler;  //连接异常断开时，产生一个事件
        private Timers.Timer checkConnectStateTime = new Timers.Timer(1000); //一秒检查一次串口连接状态，如果发现串口断开，则产生一个事件
        private double currentPower; //功率值，以dBm为单位
        #endregion

        //设备名称
        private string ProductName { set; get; }

        #region 一次只测一台设备，所以使用单例模式
        private static Otdr otdr = null;

        private Otdr() {
            serialPort = new SerialPort();
            serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceived);
            checkConnectStateTime.Elapsed += new Timers.ElapsedEventHandler(CheckConnectState);
        }

        public static Otdr GetInstance(string productName)
        {
            if (otdr == null) {
                otdr = new Otdr();
                otdr.ProductName = productName;
            }
            return otdr;
        }
        #endregion


        //数据到达事件
        private void DataReceived(object sender, SerialDataReceivedEventArgs args)
        {
            int receiveCount = serialPort.BytesToRead;
            int canReadCount = serialPort.BytesToRead;  //获取可读数据数量
            byte[] buff = new byte[canReadCount];
            serialPort.Read(buff, 0, canReadCount);  //读取串口中的数据
            //*** 将收到的字节放入清单
            foreach (byte b in buff)
            {
                receiveByteList.Add(b);
            }
            Thread analysisDataThread = new Thread(AnalysisReceiveData); //新建一个线程分析收到的数据
            analysisDataThread.IsBackground = true;  //将线程设为后台线程
            analysisDataThread.Start(); //启动线程
        }

        //*** 分析数据
        private void AnalysisReceiveData()
        {
            lock (receiveByteList)
            {
                bool headExists = false;
                do
                {
                    if (receiveByteList.Count < 13)
                    {
                        return;
                    }
                    if (receiveByteList[0] == 0xaa)
                    {
                        headExists = true;
                    }
                    else
                    {
                        receiveByteList.RemoveAt(0);
                    }
                } while (!headExists);

                int dataLen = 13; //数据长度

                //*** 如果收到的数据长度小于应有的数据长度，则返回
                if (receiveByteList.Count < dataLen)
                {
                    return;
                }


                if (receiveByteList[0] == 0xaa && receiveByteList[1] == 0x02) //切换波长命令返回值
                {
                    changeWaveEvent.Set(); //复位信号量，继续挂起的线程
                }
                else if (receiveByteList[0] == 0xaa && receiveByteList[1] == 0x01)
                {  //读取功率命令
                    float mwPower = BitConverter.ToSingle(
                        new byte[] { receiveByteList[5], receiveByteList[6], receiveByteList[7], receiveByteList[8] }, 0);
                    currentPower = Math.Round(10 * Math.Log10(mwPower), 3); //转成dbm
                    readPowerEvent.Set();
                }
                else if (receiveByteList[0] == 0xaa && receiveByteList[1] == 0x03 && receiveByteList[2] == 0x01)
                {  //进入或退出设置模式
                    setModeEvent.Set();
                }
                else if (receiveByteList[0] == 0xaa && receiveByteList[1] == 0x03 && receiveByteList[2] == 0x02)
                {  //设置线性点
                    setLinearEvent.Set();
                }
                else if(receiveByteList[0] == 0xaa && receiveByteList[1] == 0x03 && receiveByteList[2] == 0x03)
                {   //设置-10点
                    setValueEvent.Set();
                }
                else if (receiveByteList[0] == 0xaa && receiveByteList[1] == 0x03 && receiveByteList[2] == 0x04)
                {   //保存到EEPROM
                    saveValueEvent.Set();
                }
                for (int i = 0; i < 13; i++)
                {
                    receiveByteList.RemoveAt(0);
                }
            }
        }


        //如果串口没有打开，则产生一个事件
        private void CheckConnectState(object sender, Timers.ElapsedEventArgs args)
        {
            bool isOpen = serialPort.IsOpen;
            if (!isOpen)
            {
                ConnectBreakHandler?.Invoke(this, new ConnectBreakEvent());
            }
        }


        //断开连接串口
        public void Disconnect()
        {
            try
            {
                checkConnectStateTime.Stop();
                serialPort.Close();
            }
            catch { }
        }


        //连接串口
        public bool Connect()
        {
            string[] portnames = SerialPort.GetPortNames();  //获取可用串口清单
            int baudrate = 9600; //目前所有的机型波特率都是9600，所以直接在这里写死
            #region 遍历所有可用串口，发相关命令，如果有特定的返回值，则认为是被测OTDR
            foreach (string portname in portnames)
            {
                serialPort.PortName = portname;
                serialPort.BaudRate = baudrate;
                try
                {
                    serialPort.Open();
                    #region -9999为未正确读取到光功，未正确读取到光功，认为当前串口不是被测表，关闭当前串口，进入下一个串口
                    if (ReadPower() == -9999)
                    {
                        serialPort.Close();
                        continue;
                    }
                    else
                    {
                        checkConnectStateTime.Start();
                        return true;
                    }
                    #endregion
                }catch(UnauthorizedAccessException) //端口被占用的时候，跳过当前端口
                {
                    continue;
                }catch(Exception ex)
                {
                    throw ex;
                }
            }
            #endregion
            return false;
        }


        //切换波长
        public bool ChangeWavelength(string wavelength)
        {
            if (!serialPort.IsOpen)
            {
                Connect();
            }
            byte[] changeWaveBytes = new byte[] { };
            List<string> wavelengthList = ProductManagement.GetInstance().GetWaveList(ProductName);  //获取该机型的波长列表
            byte waveIndex = (byte)wavelengthList.IndexOf(wavelength.ToString()); //获取波长的索引号
            changeWaveBytes = new byte[] { 0xaa, 0x02, 0x01, 0x01, waveIndex, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
            //}
            //发送切换命令
            serialPort.Write(changeWaveBytes, 0, changeWaveBytes.Length);
            bool result = changeWaveEvent.WaitOne(3000);    //3S内收到信号量，则认为成功
            return result;
        }


        //读取光功
        public double ReadPower()
        {
            if (!serialPort.IsOpen)
            {
                return -9999;
            }
            byte[] getPowerBytes = { 0xaa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
            serialPort.Write(getPowerBytes, 0, getPowerBytes.Length);
            bool result = readPowerEvent.WaitOne(3000);  //等待信号量，3秒内没收到信号量，说明读取失败
            if (result)
            {
                return currentPower;
            }
            else
            {
                return -9999;
            }
        }


        //设置当前光功值
        public bool SetPower(double power)
        {
            if (!serialPort.IsOpen)
            {
                Connect();
            }
            float f = (float)Math.Pow(10, power / 10);
            byte[] bytes = BitConverter.GetBytes(f);
            List<byte> setPowerList = new List<byte>();
            setPowerList.Add(0xaa);
            setPowerList.Add(0x03);
            setPowerList.Add(0x03);
            setPowerList.Add(0x00);
            setPowerList.Add(0x00);
            foreach (byte b in bytes)
            {
                setPowerList.Add(b);
            }
            for (int byteCount = setPowerList.Count; byteCount < 13; byteCount++)
            {
                setPowerList.Add(0x00);
            }
            serialPort.Write(setPowerList.ToArray(), 0, setPowerList.Count);
            bool result = setValueEvent.WaitOne(3000);
            return result;
        }


        //设置线性点,传入参数为线性点索引号、点1、点2
        public bool SetLinear(int level, double power1, double power2)
        {
            if (!serialPort.IsOpen)
            {
                Connect();
            }
            #region 下位机编写的第7段使用的命令与前6段不一致
            if (level == 7)
            {
                level = 6;
            }
            #endregion
            #region 根据第几段，进入相应的设置模式
            byte[] orderBytes = { 0xaa, 0x03, 0x01, 0x01, (byte)level, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
            serialPort.Write(orderBytes, 0, orderBytes.Length);
            bool enterSetMode = setModeEvent.WaitOne(3000); //进入设置模式与退出设置模式返回值相同，所以使用同一个信号量
            if (!enterSetMode)  //如果无法进入设置模式，则返回false
            {
                return false;
            }
            #endregion
            #region 设置第1个点
            float f = (float)Math.Pow(10, power1 / 10);
            byte[] bytes = BitConverter.GetBytes(f);
            List<byte> setPowerList = new List<byte>();
            setPowerList.Add(0xaa);
            setPowerList.Add(0x03);
            setPowerList.Add(0x02);
            if (level == 7)  //第7段的命令与前6段不一样
            {
                setPowerList.Add(0xC0);
            }
            else
            {
                setPowerList.Add(0x00);
            }
            setPowerList.Add(0x00);
            foreach (byte b in bytes)
            {
                setPowerList.Add(b);
            }
            for (int byteCount = setPowerList.Count; byteCount < 13; byteCount++)
            {
                setPowerList.Add(0x00);
            }
            serialPort.Write(setPowerList.ToArray(), 0, setPowerList.Count);
            bool setPoint1 = setLinearEvent.WaitOne(3000);
            if (!setPoint1)
            {
                return false;
            }
            #endregion
            #region 设置第2个点
            f = (float)Math.Pow(10, power2 / 10);
            bytes = BitConverter.GetBytes(f);
            setPowerList.Clear();
            setPowerList.Add(0xaa);
            setPowerList.Add(0x03);
            setPowerList.Add(0x02);
            if (level == 7)
            {
                setPowerList.Add(0xC1);
            }
            else
            {
                setPowerList.Add(0x80);
            }
            setPowerList.Add(0x00);
            foreach (byte b in bytes)
            {
                setPowerList.Add(b);
            }
            for (int byteCount = setPowerList.Count; byteCount < 13; byteCount++)
            {
                setPowerList.Add(0x00);
            }
            serialPort.Write(setPowerList.ToArray(), 0, setPowerList.Count);
            bool setPoint2 = setLinearEvent.WaitOne(3000);
            if (!setPoint2)
            {
                return false;
            }
            #endregion
            #region 退出设置模式
            byte[] exitorderBytes = { 0xaa, 0x03, 0x01, 0x00, (byte)level, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
            serialPort.Write(exitorderBytes, 0, exitorderBytes.Length);
            bool exit = setModeEvent.WaitOne(3000);
            return exit;
            #endregion
        }


        //保存到EEPROM
        public bool SaveValue()
        {
            if (!serialPort.IsOpen)
            {
                Connect();
            }
            byte[] saveBytes = { 0xaa, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
            serialPort.Write(saveBytes, 0, saveBytes.Length);
            bool save = saveValueEvent.WaitOne(3000);
            return save;
        }


        //将仪表内的系数恢复为默认值
        public void Reset()
        {
            if (!serialPort.IsOpen)
            {
                Connect();
            }
            byte[] resetBytes = { 0xaa, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
            serialPort.Write(resetBytes, 0, resetBytes.Length);
        }

    }
}
